use std::{cell::Cell, rc::Rc};

use gtk::{
    glib::{self, clone::Downgrade as _},
    prelude::{
        ApplicationExt as _, ApplicationExtManual as _, BoxExt as _, ButtonExt as _,
        GtkWindowExt as _, WidgetExt as _,
    },
    subclass::{
        button::ButtonImpl,
        prelude::{ObjectImpl, ObjectImplExt, ObjectSubclass, ObjectSubclassExt as _},
        widget::WidgetImpl,
    },
    Application, ApplicationWindow, Button,
};

const APP_ID: &str = "xyz.plus7wist.HelloGtk";

fn main() -> glib::ExitCode {
    let app = Application::builder().application_id(APP_ID).build();

    app.connect_activate(build_ui);
    app.run()
}

macro_rules! try_upgrade {
    ($weak: expr) => {
        match $weak.upgrade() {
            Some(some) => some,
            None => {
                eprintln!("Failed to upgrade weak reference");
                return;
            }
        }
    };
}

glib::wrapper! {
    struct CustomButtonGlib(ObjectSubclass<CustomButton>)
        @extends
            gtk::Button,
            gtk::Widget,
        @implements
            gtk::Accessible,
            gtk::Actionable,
            gtk::Buildable,
            gtk::ConstraintTarget;
}

impl CustomButtonGlib {
    fn new(label: &str) -> Self {
        glib::Object::builder().property("label", label).build()
    }
}

#[derive(Default)]
struct CustomButton {
    number: Cell<i32>,
}

#[glib::object_subclass]
impl ObjectSubclass for CustomButton {
    const NAME: &'static str = "CustomButton";
    type Type = CustomButtonGlib;
    type ParentType = gtk::Button;
}

impl ObjectImpl for CustomButton {
    fn constructed(&self) {
        self.parent_constructed();
        self.obj().set_label(&self.number.get().to_string());
    }
}

impl WidgetImpl for CustomButton {}

impl ButtonImpl for CustomButton {
    fn clicked(&self) {
        self.number.set(self.number.get() + 1);
        self.obj().set_label(&self.number.get().to_string());
    }
}

fn build_ui(app: &Application) {
    let btn_add = Button::builder()
        .label("+")
        .margin_bottom(10)
        .margin_top(10)
        .margin_start(10)
        .margin_end(10)
        .build();

    let btn_sub = Button::builder()
        .label("-")
        .margin_bottom(10)
        .margin_top(10)
        .margin_start(10)
        .margin_end(10)
        .build();

    let n = Rc::new(Cell::new(0i32));

    btn_add.connect_clicked({
        let n = n.clone();
        let btn_sub = btn_sub.downgrade();

        move |_| {
            let btn_sub = try_upgrade!(btn_sub);

            n.set(n.get() + 1);
            btn_sub.set_label(&n.get().to_string());
        }
    });

    btn_sub.connect_clicked({
        let btn_add = btn_add.downgrade();

        move |_| {
            let btn_add = try_upgrade!(btn_add);

            n.set(n.get() - 1);
            btn_add.set_label(&n.get().to_string());
        }
    });

    let cbtn = CustomButtonGlib::new("Press Me");

    cbtn.set_margin_start(10);
    cbtn.set_margin_end(10);
    cbtn.set_margin_top(10);
    cbtn.set_margin_bottom(10);

    let gbox = gtk::Box::builder()
        .orientation(gtk::Orientation::Vertical)
        .build();

    gbox.append(&btn_add);
    gbox.append(&btn_sub);
    gbox.append(&cbtn);

    // Create a window and set the title
    let window = ApplicationWindow::builder()
        .application(app)
        .title("Hello GTK")
        .child(&gbox)
        .build();

    // Present window
    window.present();
}
